<template>
  <a-card title="楼房分户" style="width: 600px; pointer-events: all">
    <template #extra>
      <SyncOutlined @click="refresh" />
    </template>
    <a-steps :current="currentStep" :items="steps"></a-steps>
    <div class="draw-area">
      <span>绘制户型:</span>
      <EditOutlined class="icon-draw" @click="activeDraw" />
      <span>{{ textDraw }}</span>
    </div>
    <div class="form-area">
      <a-table size="small" bordered :data-source="buildingData" :columns="buildingColumns" :pagination="false">
        <template #bodyCell="{ column, text, record }">
          <!-- 只作用域地址前缀和单位这两列 -->
          <template v-if="['positionPrefix', 'unit'].includes(column.dataIndex)">
            <div>
              <!-- 如果 editableData[record.key] 存在（说明这行是正在编辑的行） 并将其值绑定到这个单元格的编辑值,否则显示原来的文本-->
              <a-input v-if="editableData[record.key]" v-model:value="editableData[record.key][column.dataIndex]" style="margin: -5px 0" />
              <template v-else>
                {{ text }}
              </template>
            </div>
          </template>

          <!-- 只有做区域绘制的时候，需要编辑 -->
          <template v-else-if="column.dataIndex === 'operation' && currentStep !== 2">
            <div class="editable-row-operations">
              <span v-if="editableData[record.key]" style="display: flex; justify-content: space-around">
                <!-- 排版风格的超链接文本,点击时执行save() -->
                <a-typography-link @click="save(record.key)">保存</a-typography-link>
                <!-- 气泡确认框 -->
                <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
                  <a>取消</a>
                </a-popconfirm>
              </span>
              <span v-else>
                <a @click="edit(record.key)">编辑</a>
              </span>
            </div>
          </template>

          <!-- 只有户型切分的时候，需要定位 -->
          <template v-if="['positionFix'].includes(column.dataIndex)">
            <div @click="focusOnArea(record.key)">
              <img style="cursor: pointer" width="20" height="20" src="/src/assets/position.png" alt="" />
            </div>
          </template>
        </template>
      </a-table>
    </div>
    <!-- 楼房分层 -->
    <div class="floor-area" v-if="currentStep === 2 && showDivide">
      <span>最低点:</span>
      <a-input width="100" :value="minHeight" :min="-100" :max="200" :disabled="true"></a-input>
      <span>分割点:</span>
      <a-input width="100" :value="divideHeight" :min="-100" :max="200" :disabled="true"></a-input>
      <span>最高点:</span>
      <a-input width="100" :value="maxHeight" :min="-100" :max="200" :disabled="true"></a-input>
      <span>楼层数:</span>
      <a-input width="100" v-model:value="floorNum" :min="0" :max="100"></a-input>
    </div>
    <div class="button-area">
      <a-button v-if="next" type="primary" @click="nextStep">下一步</a-button>
      <a-button v-if="showDivide" @click="divideFloor" style="margin: 0 10px">楼房分层</a-button>
      <a-button v-if="currentStep == steps.length - 1 && updateActive" type="primary" @click="updateInfo">生成数据</a-button>
    </div>
  </a-card>
</template>

<script setup>
import * as Cesium from 'cesium'
import { ref, reactive, computed, watch, onBeforeUnmount } from 'vue'
import { cloneDeep } from 'lodash'
import { binkEntityMaterial } from '@/cesiumTools/sceneManager.js'
import { changeCartesin3ToGeoJson, polygonCut, cartesian3ToDegreesHeight } from '@/cesiumTools/utils'
import DrawTool from '../cesiumTools/drawTool'
import { SyncOutlined, EditOutlined } from '@ant-design/icons-vue'
import { useMapData } from '@/store'
import { addHouse } from '@/api/api'
import { message } from 'ant-design-vue'

const mapStore = useMapData()
const viewer = mapStore.Viewer
const drawTool = new DrawTool(viewer)
const currentStep = ref(0)
const showDivide = ref(false)
const updateActive = ref(false)
const minHeight = ref(0)
const divideHeight = ref(0)
const maxHeight = ref(0)
const floorNum = ref(0)
const next = ref(false)

const refresh = () => {
  currentStep.value = null
  buildingData.value = []
  viewer.entities.removeAll()
  primitivesArr.length &&
    primitivesArr.forEach((item) => {
      viewer.scene.primitives.remove(item)
    })
  primitivesArr.length = 0
  floorNum.value = 0
  updateActive.value = false
  showDivide.value = false
}

const steps = ref([
  {
    title: '区域绘制',
    key: '区域绘制',
  },
  {
    title: '户型切分',
    key: '户型切分',
  },
  {
    title: '楼房分层',
    key: '楼房分层',
  },
])

const textDraw = computed(() => {
  let res = '请点击图标绘制'
  switch (currentStep.value) {
    case 1:
      res = '请点击图标裁剪'
      break
    case 2:
      res = '请点击图标分层'
      break
    default:
      break
  }
  return res
})

// 这里是推送到服务端的数据
// polygonJson为总区域geojson字符串
// polygonJsonArr为切割后geojson字符串数组，如果没有切割，传[]
// unitArr 单位数组
// heightArr 三个切分点高度数组
// name 楼栋名称
// floorNum楼层数
const postDataSource = {
  polygonJson: '',
  polygonJsonArr: [],
  unitArr: [],
  heightArr: [],
  name: '',
  floorNum: 0,
}
// 户型设置
const buildingData = ref([])
const buildingColumns = ref([
  {
    title: '分户坐标',
    dataIndex: 'position',
    align: 'center',
    width: '30%',
    // 超出宽度显示省略号
    ellipsis: true,
  },
  {
    title: '地址前缀',
    dataIndex: 'positionPrefix',
    align: 'center',
    width: '30%',
    ellipsis: true,
  },
  {
    title: '单位',
    dataIndex: 'unit',
    align: 'center',
  },
  {
    title: '操作',
    dataIndex: 'operation',
    align: 'center',
  },
])

const editableData = reactive({})
// 可编辑表格配置
// 根据传入的 key，从 buildingData 中找到对应的数据项，然后深拷贝到 editableData 中对应的位置，准备进行编辑。
const edit = (key) => {
  editableData[key] = cloneDeep(buildingData.value.filter((item) => key === item.key)[0])
}
// Object.assign(target, source) 将 source 对象的属性复制到 target 对象中，会修改 target 本身。
// delete editableData[key] 保存完成后，删除编辑缓存，代表该项已经保存，不再处于“编辑中”状态。
const save = (key) => {
  Object.assign(buildingData.value.filter((item) => key === item.key)[0], editableData[key])
  delete editableData[key]
}
const cancel = (key) => {
  delete editableData[key]
}
// 卸载前移除监听事件并刷新
onBeforeUnmount(() => {
  drawEntLisener && drawTool.removeListener(drawEntLisener)
  refresh()
})
// 根据步骤变化动态增删表格列
watch(currentStep, (val) => {
  if (val === 1) {
    next.value = false
    if (!buildingColumns.value.find((item) => item.dataIndex === 'positionFix')) {
      buildingColumns.value.push({
        title: '定位',
        dataIndex: 'positionFix',
        align: 'center',
        width: '10%',
      })
    }
  }
  // 楼房分层，将后面两列删除
  if (val === 2) {
    buildingColumns.value = buildingColumns.value.filter((item) => item.dataIndex !== 'positionFix' && item.dataIndex !== 'operation')
  }
})

const nextStep = () => {
  // 只有区域绘制数据有值的时候，才能往下走
  if (buildingData.value?.length) {
    currentStep.value++
  } else {
    message.warn('请先绘制区域')
  }
  console.log(currentStep.value)
}

// 上传服务
const updateInfo = async () => {
  const unitArr = buildingData.value.map((item) => {
    return Number(item.unit)
  })
  const heightArr = [minHeight.value, divideHeight.value, maxHeight.value]
  const name = buildingData.value[0]?.positionPrefix
  postDataSource.unitArr = unitArr
  postDataSource.heightArr = heightArr
  postDataSource.name = name
  postDataSource.floorNum = Number(floorNum.value)
  const res = await addHouse(postDataSource)
  if (res.code === 200) {
    refresh()
    message.success('分户成功')
  }
}

// 定位至区域
const focusOnArea = (key) => {
  const targetEntity = viewer.entities.values.find((item) => item.name === key)
  if (targetEntity) {
    binkEntityMaterial(targetEntity)
  }
}

// 设置楼层
const primitivesArr = []
const divideFloor = () => {
  if (floorNum.value.length) {
    //计算每一层的高度，即每一层楼的层高
    let itemHeight = (maxHeight.value - divideHeight.value) / (floorNum.value - 1)
    viewer.entities.removeAll()
    buildingData.value.forEach((item) => {
      const { position } = item
      console.log(position)
      const positionArr = position.split(',').map((item) => Number(item))
      // 根据楼层数 floorNum.value 计算每一层楼的 height（底部高度）和 extrudedHeight（拉伸高度）。
      for (let i = 0; i < floorNum.value; i++) {
        let height, extrudedHeight
        if (i == 0) {
          height = minHeight.value
          extrudedHeight = divideHeight.value
        } else {
          height = divideHeight.value + (i - 1) * itemHeight
          extrudedHeight = divideHeight.value + i * itemHeight
        }

        // 生成分层的多边形外壳遮罩
        let primitive = new Cesium.ClassificationPrimitive({
          geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.PolygonGeometry({
              // 边界结构
              polygonHierarchy: new Cesium.PolygonHierarchy(new Cesium.Cartesian3.fromDegreesArray(positionArr)),
              height: height,
              extrudedHeight: extrudedHeight,
            }),
            attributes: {
              color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                Cesium.Color.fromRandom({ alpha: 0.3 }) //颜色
              ),
            },
          }),
          // 分类对象只影响 3D Tiles 模型。
          classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
        })
        primitivesArr.push(primitive)
        viewer.scene.primitives.add(primitive)
        updateActive.value = true
      }
    })
  }
}

let lastDrawEnt
let drawEntLisener
let polygonGeo

// 绘制图形逻辑
const activeDraw = () => {
  // 先移除之前可能存在的监听器，防止重复触发
  drawEntLisener && drawTool.removeListener(drawEntLisener)

  if (currentStep.value === 0) {
    // 重新绘制，清除之前绘制的图形
    drawTool.removeAllDrawEnts()

    // 定义绘制结束监听器
    drawEntLisener = (drawEnt, postions, drawType) => {
      const { geojson, positionLng } = changeCartesin3ToGeoJson(postions, 'polygon')
      if (geojson) {
        polygonGeo = geojson
        // 在table中填入数据
        buildingData.value[0] = {
          position: positionLng.join(','),
          positionPrefix: 'xxx小区xxx楼栋',
          unit: '1',
        }
        lastDrawEnt = drawEnt
        // 传入总区域数据
        postDataSource.polygonJson = JSON.stringify(geojson.geometry)

        // 移除监听器，防止重复触发
        drawEntLisener && drawTool.removeListener(drawEntLisener)

        next.value = true
      }
    }

    // 注册绘制结束监听器
    drawTool.DrawEndEvent.addEventListener(drawEntLisener)

    // 激活多边形绘制
    drawTool.active(drawTool.DrawTypes.Polygon)
  } else if (currentStep.value === 1) {
    // 进行户型裁剪
    // 定义绘制结束监听器
    drawEntLisener = (drawEnt, postions) => {
      if (postions?.length) {
        drawTool.removeAllDrawEnts()

        const { geojson: lineJson } = changeCartesin3ToGeoJson(postions, 'polyline')
        let polygonCollection

        try {
          // 把原始多边形（楼房）和绘制的线都转换为数字格式，防止类型错误
          polygonGeo.geometry.coordinates[0] = polygonGeo.geometry.coordinates[0].map((item) => item.map((n) => Number(n)))
          lineJson.geometry.coordinates = lineJson.geometry.coordinates.map((item) => item.map((n) => Number(n)))

          polygonCollection = polygonCut(polygonGeo, lineJson)

          // 获取到裁剪之后的geojson数据
          if (polygonCollection.features.length == 1) {
            message.error('请切割区域！')
            return
          }
        } catch (error) {
          message.error(error)
          return
        }

        if (polygonCollection) {
          // 将裁剪数据传入
          postDataSource.polygonJsonArr = polygonCollection.features.map((item) => JSON.stringify(item.geometry))

          // 使用dataSource加载多边形数据
          Cesium.GeoJsonDataSource.load(polygonCollection, { clampToGround: true }).then((info) => {
            lastDrawEnt && viewer.entities.remove(lastDrawEnt)
            const dataSource = []
            lastDrawEnt = null

            info.entities.values.forEach((item, index) => {
              item.polygon.material = Cesium.Color.fromRandom({ alpha: 0.5 })
              viewer.entities.add(item)
              item.name = index

              dataSource.push({
                key: index,
                position: polygonCollection.features[index].geometry.coordinates.toString(),
                positionPrefix: buildingData.value[0].positionPrefix,
                unit: index + 1,
              })
            })

            buildingData.value = dataSource

            // 移除监听器，防止重复触发
            drawEntLisener && drawTool.removeListener(drawEntLisener)

            next.value = true
          })
        }
      }
    }

    // 注册绘制结束监听器
    drawTool.DrawEndEvent.addEventListener(drawEntLisener)

    // 激活折线绘制
    drawTool.active(drawTool.DrawTypes.Polyline)
  } else {
    // 绘制高度点以实现楼房分层
    // 定义绘制结束监听器
    drawEntLisener = (drawEnt, positions) => {
      if (positions.length === 3) {
        let positionsPerHeight = positions.map((item) => {
          const positionLng = cartesian3ToDegreesHeight(item)
          return positionLng[2]
        })

        positionsPerHeight.sort((a, b) => a - b)
        minHeight.value = positionsPerHeight[0]
        divideHeight.value = positionsPerHeight[1]
        maxHeight.value = positionsPerHeight[2]
        showDivide.value = true
      } else {
        message.warn('请绘制底层，二楼以及顶楼高度点')
        return
      }

      // 移除监听器，防止重复触发
      drawEntLisener && drawTool.removeListener(drawEntLisener)
    }

    // 注册绘制结束监听器
    drawTool.DrawEndEvent.addEventListener(drawEntLisener)

    // 激活点绘制
    drawTool.active(drawTool.DrawTypes.Point)
  }
}
</script>
<style scoped>
.button-area {
  display: flex;
  justify-content: flex-end;
  margin-top: 20px;
}

.draw-area {
  margin: 20px 0;
}

.draw-area span {
  margin: 0 4px;
}

.icon-draw {
  padding: 4px;
}

.icon-draw:hover {
  cursor: pointer;
  background-color: skyblue;
  color: #fff;
}

.floor-area span {
  margin: 0 10px;
}
</style>
